package com.mapforge.selection;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * The {@link CheckedCompositeSelection} class represents a collection of
 * selections. Each time a selection is added to this {@link Selection} via
 * {@link #add(Selection)} any intersections will be calculated and resolved so
 * the same point will not be returned twice by this selection.
 * 
 * @see UncheckedCompositeSelection
 * @author Will Morrison
 * 
 */
public class CheckedCompositeSelection implements Selection {

	private Rectangle boundingRect;
	private List<Selection> selections;

	public CheckedCompositeSelection() {
		boundingRect = new Rectangle();
		selections = new ArrayList<Selection>();
	}

	@Override
	public Rectangle getBoundingRectangle() {
		return (Rectangle) boundingRect.clone();
	}

	@Override
	public void setOrigin(int x, int y) {
		for (Selection selection : selections) {
			selection.setOrigin(x, y);
		}
	}

	@Override
	public Iterator<ComparablePoint> iterator() {
		return new MultiSelectionIterator(selections, null);
	}

	@Override
	public Iterator<ComparablePoint> iterator(Rectangle rectangle) {
		return new MultiSelectionIterator(selections, rectangle);
	}

	@Override
	public void clearSelection() {
		selections.clear();
		boundingRect.setFrame(0, 0, 0, 0);
	}

	@Override
	public boolean add(Selection other) {
		Rectangle rect = other.getBoundingRectangle();
		boolean intersection = false;
		// first selection ever
		if (this.isEmpty()) {
			selections.add(other);
			this.boundingRect = other.getBoundingRectangle();
			System.out
					.println("No previous selection, just add new selection, bounding rect =  "
							+ boundingRect);
			return true;
		}
		// there was a previous selection
		else {
			// check for intersection w/previous selection
			for (int i = 0; i < selections.size() && !intersection; i++) {
				intersection = selections.get(i).getBoundingRectangle()
						.intersects(rect);
			}
			// there was an intersection, create new selection
			if (intersection) {
				Iterator<ComparablePoint> it = other.iterator();
				DiscontinuousSelection selection = new DiscontinuousSelectionSet();
				ComparablePoint p;
				while (it.hasNext()) {
					p = it.next();
					if (!this.contains(p.x, p.y)) {
						selection.addPoint(p);
					}
				}
				// was overlap of bounding rectangles, but no real intersection
				if (!selection.isEmpty()) {
					selections.add(selection);
					boundingRect.add(selection.getBoundingRectangle());
					return true;
				} else {
					return false;
				}
			}
			// no intersection with new selection
			else {
				selections.add(other);
				this.boundingRect.add(other.getBoundingRectangle());
				return true;
			}
		}
	}

	@Override
	public boolean subtract(Selection other) {
		System.out.println("Subtract " + other
				+ " from CompositeSelection; selection size = "
				+ selections.size());
		boolean altered = false;
		Selection selection;
		for (int i = 0; i < selections.size(); i++) {
			selection = selections.get(i);
			try {
				if (altered) {
					selection.subtract(other);
				} else {
					altered = selection.subtract(other);
				}
			} catch (UnsupportedOperationException e) {
				selections.remove(i);
				System.out.println("Remove " + selection);
				DiscontinuousSelectionCollection disSelection = new DiscontinuousSelectionSet();
				for (ComparablePoint p : selection) {
					if (!other.contains(p.x, p.y)) {
						System.out.println("Add " + p);
						disSelection.addPoint(p);
					}
				}
				selections.add(i, disSelection);
				System.out.println("add " + disSelection + " size now = "
						+ selections.size());
			}
		}
		return altered;
	}

	@Override
	public boolean intersect(Selection other) {
		boolean altered = false;
		for (Selection selection : selections) {
			if (!altered) {
				altered = selection.intersect(other);
			} else {
				selection.intersect(other);
			}
		}
		return altered;
	}

	@Override
	public boolean intersects(Selection other) {
		for (Selection selection : selections) {
			if (selection.intersects(other)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean contains(int x, int y) {
		for (Selection selection : selections) {
			if (selection.contains(x, y)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean isEmpty() {
		return this.selections.isEmpty();
	}

	@Override
	public Selection addition(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection subtraction(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Selection intersection(Selection other) {
		// TODO Auto-generated method stub
		return null;
	}
}
